package handler

import (
	"context"
	"github.com/golang/protobuf/ptypes/empty"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/modules/item/configuration"
	"kop/modules/item/consts"
	"kop/modules/item/entity"
	"kop/modules/item/models"
	"kop/pb"
	"kop/pb/service"

	activityC "kop/modules/activity/client"
	awardM "kop/modules/award/models"
	captainC "kop/modules/captain/client"
	maidC "kop/modules/maid/client"
	roleC "kop/modules/role/client"
)

type ItemServiceHandler struct {
	context2.Context
}

func (h *ItemServiceHandler) GetItems(ctx context.Context, _ *empty.Empty, resp *service.Items) error {

	var items = models.GetItems(database.DB, h.GetRoleID(ctx))

	resp.Items = make([]*pb.Item, 0, len(items))
	for _, v := range items {
		resp.Items = append(resp.Items, buildPbItem(v))
	}

	return nil
}

func (h *ItemServiceHandler) GetItem(_ context.Context, req *service.DefaultRequest, resp *service.ItemOption) error {

	var item = models.GetItem(database.DB, req.RoleID, req.Value)
	if item != nil {
		resp.Item = buildPbItem(item)
	}

	return nil
}

const RumMax = 200 // 朗姆酒上限200个

func (h *ItemServiceHandler) Add(ctx context.Context, req *service.ItemRequest, resp *pb.Item) error {

	if req.RawItem.Number < 1 {
		return errors.ParamError
	}

	var conf = configuration.GetByItemID(req.RawItem.ItemID)
	if conf.Type == configuration.Captain { // 道具转船长

		var role = roleC.GetRole(req.RoleID)
		var res = captainC.CreateCaptain(context2.NewContext(context2.NewUser(req.RoleID, role.ServerID)), conf.ItemID-105000)
		if captain := res.GetCaptain(); captain != nil {

			_ = broker.RolePublish(req.RoleID, pb.WsCode_CodeCaptain, captain)
		} else if awardItem := res.GetAwardItem(); awardItem != nil {

			*resp = *awardItem.Item
		}

		return nil

	} else if conf.Type == configuration.Maid { // 道具转侍女

		var maid, err = maidC.Create(req.RoleID, conf.ItemID-105000)
		if err == nil {
			_ = broker.RolePublish(req.RoleID, pb.WsCode_CodeMaid, maid)
		}

		return err
	}

	var item = models.GetItem(database.DB, req.RoleID, req.RawItem.ItemID)
	if item == nil {
		//if req.RawItem.ItemID == consts.Rum && req.RawItem.Number > RumMax {
		//	req.RawItem.Number = RumMax
		//}

		item = models.Create(database.DB, req.RoleID, req.RawItem.ItemID, req.RawItem.Number)

		go models.Log(ctx, req.RoleID, item.ItemID, 0, item.Number)
	} else {

		var before = item.Number

		item.Number += req.RawItem.Number
		if item.ItemID == consts.Rum && item.Number > RumMax {
			item.Number = RumMax
		}
		models.Save(database.DB, item)

		go models.Log(ctx, req.RoleID, item.ItemID, before, item.Number)
	}

	*resp = *buildPbItem(item)
	_ = broker.RolePublish(req.RoleID, pb.WsCode_CodeItem, resp)
	return nil
}

func (h *ItemServiceHandler) Del(ctx context.Context, req *service.ItemRequest, resp *pb.Item) error {

	if req.RawItem.Number < 1 {
		return errors.ParamError
	}

	var item = models.GetItem(database.DB, req.RoleID, req.RawItem.ItemID)
	if item == nil {

		switch req.RawItem.ItemID {
		case consts.Gold:
			return errors.GoldNotEnoughError
		case consts.Diamond:
			return errors.DiamondNotEnoughError
		default:
			return errors.ItemNotEnoughError
		}
	}

	if item.Number < req.RawItem.Number {

		switch req.RawItem.ItemID {
		case consts.Gold:
			return errors.GoldNotEnoughError
		case consts.Diamond:
			return errors.DiamondNotEnoughError
		default:
			return errors.ItemNotEnoughError
		}

	} else {

		var before = item.Number

		item.Number -= req.RawItem.Number
		models.Save(database.DB, item)

		go models.Log(ctx, req.RoleID, item.ItemID, before, item.Number)
	}

	*resp = *buildPbItem(item)

	switch req.RawItem.ItemID {
	case consts.Gold: // quest of activity
		activityC.AddPoints(req.RoleID, req.ServerID, activityC.Gold, int64(req.RawItem.Number))
	case consts.Diamond:
		activityC.AddPoints(req.RoleID, req.ServerID, activityC.Diamond, int64(req.RawItem.Number))
	}

	return nil
}

func (h *ItemServiceHandler) AsyncDel(ctx context.Context, req *service.ItemRequest, _ *empty.Empty) (err error) {

	var item pb.Item
	if err = h.Del(ctx, req, &item); err == nil {
		_ = broker.RolePublish(req.RoleID, pb.WsCode_CodeItem, &item)
	}

	return err
}

//func (h *ItemServiceHandler) Save(_ context.Context, req *service.SaveItemRequest, resp *pb.Item) error {
//
//	*resp = *req.Item
//	var item = entity.Item{
//		ID:     resp.ID,
//		RoleID: req.RoleID,
//		ItemID: resp.ItemID,
//		Number: resp.Number,
//	}
//
//	return models.Save(database.DB, &item)
//}

//func (h *ItemServiceHandler) AsyncSave(ctx context.Context, req *service.SaveItemRequest, _ *empty.Empty) (err error) {
//
//	if err = h.Save(ctx, req, req.Item); err == nil {
//		_ = broker.RolePublish(req.RoleID, pb.WsCode_CodeItem, req.Item)
//	}
//
//	return err
//}

func (h *ItemServiceHandler) Package(ctx context.Context, req *pb.RawItem, resp *pb.Award) error {

	var roleID = h.GetRoleID(ctx)

	if req.Number < 1 {
		return errors.ParamError
	}

	var item = models.GetItem(database.DB, roleID, req.ItemID)
	if item == nil || item.Number < req.Number {
		return errors.ItemNotEnoughError
	}

	var conf = configuration.GetByItemID(item.ItemID)
	if conf.Type != configuration.TypePackage {
		return errors.ParamError
	}

	item.Number -= req.Number
	models.Save(database.DB, item)

	var role = roleC.GetRole(roleID)

	for i := 0; i < int(req.Number); i++ {
		resp.Merge(*awardM.RandAward(role, conf.AwardID))
	}

	if err := awardM.SendAward(role, resp); err != nil {
		return err
	}

	resp.AwardItems = append(resp.AwardItems, &pb.AwardItem{Item: buildPbItem(item)})
	return nil
}

func (h *ItemServiceHandler) Compose(ctx context.Context, req *pb.RawItem, resp *pb.Award) error {

	var itemID, number = configuration.ComposeByItemID(req.ItemID)
	number *= req.Number

	if req.Number < 1 {
		return errors.ParamError
	}

	var roleID, serverID, delItem = h.GetRoleID(ctx), h.GetServerID(ctx), &pb.Item{}
	if err := h.Del(ctx, &service.ItemRequest{RoleID: roleID, ServerID: serverID, RawItem: &pb.RawItem{ItemID: itemID, Number: number}}, delItem); err != nil {
		return err
	}

	var item = &pb.Item{}
	if err := h.Add(ctx, &service.ItemRequest{RoleID: roleID, ServerID: serverID, RawItem: req}, item); err != nil {
		return err
	}

	resp.AwardItems = []*pb.AwardItem{{RawItem: req, Item: item}, {Item: delItem}}
	return nil
}

//func (h *ItemServiceHandler) AddAfterSub(_ context.Context, req *service.AddAfterSubRequest, resp *service.Items) (err error) {
//
//	_, err = configuration.GetByItemID(req.AddItem.ItemID)
//	if err != nil {
//		return err
//	}
//
//	var session = database.DB.Begin()
//	if session.Error != nil {
//		return session.Error
//	}
//
//	defer func() {
//		if r := recover(); r != nil {
//			err = r.(error)
//		}
//		if err != nil {
//			session.Rollback()
//		}
//	}()
//
//	subItem, err := models.GetItem(session, req.RoleID, req.SubItem.ItemID)
//	if err != nil {
//		return err
//	}
//
//	if subItem.Number < req.SubItem.Number {
//		return errors.ItemNotEnoughError
//	}
//
//	subItem.Number -= req.SubItem.Number
//	if err = models.Save(session, req.RoleID, &subItem); err != nil {
//		return err
//	}
//
//	var addItem = models.Add(database.DB, req.RoleID, req.AddItem.ItemID, req.AddItem.Number)
//
//	resp.Items = []*pb.Item{&subItem, &addItem}
//	return session.Commit().Error
//}
func buildPbItem(item *entity.Item) *pb.Item {

	return &pb.Item{
		ID:       item.ID,
		ItemID:   item.ItemID,
		Number:   item.Number,
		ItemType: int32(configuration.GetByItemID(item.ItemID).Type),
	}
}
